package com.newlandedu;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 用途
 *
 * @author deng
 * @date 2021/5/17 14:48
 */
public class PapeDataListener extends AnalysisEventListener<Paper> {


    private static final Logger LOGGER = LoggerFactory.getLogger(AnalysisEventListener.class);
    Map<String, List<Paper>> map = new LinkedHashMap<String, List<Paper>>();

    // 默认中级
    String level = "中级";

    // 随机数种子
    private static Random random = new Random(9999);
    private static long[] seed = new long[]{591620, 485204, 492524, 221629, 220559, 163657, 356152, 611705, 404583, 164149};
    static int _i = 0;

    public PapeDataListener(String level) {
        this.level = level;
    }

    @Override
    public void invoke(Paper data, AnalysisContext context) {
        trim(data);
        if (!map.containsKey(data.getCode())) {
            List<Paper> list = new ArrayList<Paper>();
            list.add(data);
            map.put(data.getCode(), list);

        } else {
            List<Paper> list = map.get(data.getCode());
            list.add(data);
            list.sort((o1, o2) ->
                    o1.getTypecode().equals(o2.getTypecode()) ? 1 :
                            o1.getTypecode().compareTo(o1.getTypecode()));
        }


    }

    // 去除空格，如果是null,改成空字符串
    private void trim(Paper data) {
        Field[] fields = data.getClass().getDeclaredFields();
        for (Field field : fields) {
            Class<?> clasz = field.getType();
            if (clasz.equals(String.class)) {
                try {
                    String value = (String) field.get(data);
                    if (value == null) {
                        value = "";
                    }
                    // 括号替换
                    value = replaceBracket(value);
                    field.set(data, value.trim());

                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 去除括号和换行
     *
     * @param text
     * @return
     */
    private static String replaceBracket(String text) {
        if (!StringUtils.isEmpty(text)) {
            text = text.replaceAll("（\\s*）", "（  ）");
            text = text.replaceAll("\\(\\s*\\)", "（  ）");
            text = text.replaceAll("\\(　*\\)", "（  ）");
            text = text.replaceAll("（　*）", "（  ）");
            // 去除换行
            text = text.replaceAll("\n", "");
        }
        return text;
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {


        if (level.equals("中级")) {
            handlerMiddle();
        }
        // 高级
        else {
            handlerHigh();

        }
    }


    // 处理中级
    private void handlerMiddle() {
        // 总的题目
        List<List<Paper>> topics = new ArrayList<>();
        // 循环6次
        for (int i = 0; i < 6; i++) {
            // 这一轮被选中的认证点
            Map<String, String> pickOn = new HashMap<>();
            // 这一轮被选中的题目
            List<Paper> topic = new ArrayList<>();
            topics.add(topic);
            // 题型
            String type = "B（单选题）";
            AtomicInteger time = new AtomicInteger(0);
            for (int j = 0; j < 160; j++) {
                choseTopic(pickOn, type, time, topic);
            }

            // 题型
            type = "C（判断题）";
            time = new AtomicInteger(0);
            for (int j = 0; j < 40; j++) {
                choseTopic(pickOn, type, time, topic);
            }
            // 排序，尽量序号小的排在前面
            topic.sort((o1, o2) ->
                    o1.getTypecode().equals(o2.getTypecode()) ? o1.getSerialNum().compareTo(o2.getSerialNum()) : o1.typecode.compareTo(o2.getTypecode()));
            //LOGGER.info("这一轮{},被选中的题目是{}", i, JSONObject.toJSONString(topic));
            LOGGER.info("第{}轮试卷", i + 1);
            //LOGGER.info("{}",JSONObject.toJSONString(topic));
            print(topic);

        }
        printPlain(topics);
    }


    /**
     * 处理高级
     */
    private void handlerHigh() {
        // 总的题目
        List<List<Paper>> topics = new ArrayList<>();
        // 循环6次
        for (int i = 0; i < 6; i++) {
            // 这一轮被选中的认证点
            Map<String, String> pickOn = new HashMap<>();
            // 这一轮被选中的题目
            List<Paper> topic = new ArrayList<>();
            topics.add(topic);
            // 题型
            String type = "B（单选题）";
            AtomicInteger time = new AtomicInteger(0);
            for (int j = 0; j < 120; j++) {
                choseTopic(pickOn, type, time, topic);
            }


            // 题型
            type = "D（多选题）";
            time = new AtomicInteger(0);
            for (int j = 0; j < 20; j++) {
                choseTopic(pickOn, type, time, topic);
            }

            // 题型
            type = "C（判断题）";
            time = new AtomicInteger(0);
            for (int j = 0; j < 40; j++) {
                choseTopic(pickOn, type, time, topic);
            }
            // 排序，尽量序号小的排在前面
            topic.sort((o1, o2) ->
                    o1.getTypecode().equals(o2.getTypecode()) ?
                            o1.getSerialNum().compareTo(o2.getSerialNum()) :
                            generateTypecode(o1) - generateTypecode(o2));
            //LOGGER.info("这一轮{},被选中的题目是{}", i, JSONObject.toJSONString(topic));
            LOGGER.info("第{}轮试卷", i + 1);
            print(topic);

        }
        printPlain(topics);
    }


    // 打印
    void print(List<Paper> topic) {
        for (int i = 0; i < topic.size(); i++) {
            String fmt = "%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t";
            Paper paper = topic.get(i);
            // 处理数据
            handlerData(paper);
            if (paper.getTypecode().equals("B（单选题）")) {
                printSingle(paper, i + 1);
            } else if (paper.getTypecode().equals("C（判断题）")) {
                printTrue(paper, i + 1);
            } else if (paper.getTypecode().equals("D（多选题）")) {
                printMultiple(paper, i + 1);
            }
            //System.out.println(value);

        }
        // 打印答案
        printAnswer(topic);

    }

    /**
     * 打印组卷方案
     * @param topics
     */
    void printPlain(List<List<Paper>> topics) {
        // 试卷长度
        int listsize = topics.get(0).size();
        // 认定点代码
        Set<String> keys = map.keySet();
        for (String key : keys) {
            String value = "";
            for (List<Paper> paperList : topics) {
                Optional<Paper> options = paperList.stream().filter(p -> p.getCode().equals(key)).findFirst();
                if (options.isPresent()) {
                    Paper paper = options.get();
                    // 题号
                    String qid = getQid(paper, paperList);
                    value += qid;
                }
                value += "\t";

            }
            System.out.println(value);
        }

    }

    /**
     * 获取题号
     *
     * @param paper
     * @param paperList
     * @return
     */
    private String getQid(Paper paper, List<Paper> paperList) {
        int c = 0;
        for (Paper p : paperList) {
            // 题型代码相同表示可以计数
            if (p.getTypecode().equals(paper.getTypecode())) {
                c++;
            }
            // 找到了，中止
            if (paper.equals(p)) {
                break;
            }
        }
        return paper.getTypecode().substring(0, 1) + c;

    }

    /**
     * 排序使用,单选-》多选》判断
     *
     * @param paper
     * @return
     */
    private int generateTypecode(Paper paper) {
        if (paper.getTypecode().equals("B（单选题）")) {
            return 1;
        }
        if (paper.getTypecode().equals("D（多选题）")) {
            return 2;
        }
        if (paper.getTypecode().equals("C（判断题）")) {
            return 3;
        }
        return 0;

    }

    // 打印答案
    void printAnswer(List<Paper> topic) {
        String text = "";
        // 序号
        String num = "";
        for (int i = 1; i <= topic.size(); i++) {
            Paper paper = topic.get(i - 1);
            text += toAnswer(paper);
            num += i;
            // 10个要换行
            if (i > 0 && i % 10 == 0) {
                System.out.println(num);
                System.out.println(text);
                text = "";
                num = "";
            } else {
                text += "\t";
                num += "\t";
            }
        }

    }

    private String toAnswer(Paper paper) {
        if (paper.getTypecode().equals("C（判断题）")) {
            if (paper.getAnswer().equals("正确")) {
                return "√";
            } else if (paper.getAnswer().equals("错误")) {
                return "×";
            } else {
                LOGGER.error("题库出错了，{}", JSONObject.toJSONString(paper));
            }
        }
        return paper.getAnswer();
    }

    // 单项选择题
    void printSingle(Paper paper, int num) {
        String fmt = "%d. %s";
        String question = paper.getQuestion();

        System.out.println(String.format(fmt, num, question));
        // 放在一行
        int length = countText(paper.optionA, paper.optionB, paper.optionC, paper.optionD);
        int onelength = countText(paper.optionA, paper.optionB);
        int twolength = countText(paper.optionC, paper.optionD);
        int Alength = countText(paper.optionA);
        int Clength = countText(paper.optionC);
        // 选项较小
        if (length <= 60) {
            fmt = "  A、%s  B、%s  C、%s  D、%s";
            System.out.println(String.format(fmt, paper.optionA, paper.optionB, paper.optionC, paper.optionD));
        }
        // 两行格式
        else if (length <= 120 && onelength <= 60 && twolength <= 60 && Alength < 30 && Clength < 30 && false) {
            fmt = "   A、%sB、%s";
            System.out.println(String.format(fmt, addBlank(paper.optionA, 30), paper.optionB));
            fmt = "   C、%sD、%s";
            System.out.println(String.format(fmt, addBlank(paper.optionC, 30), paper.optionD));

        }
        // 4行格式
        else {
            fmt = "   A、%s";
            System.out.println(String.format(fmt, paper.optionA));
            fmt = "   B、%s";
            System.out.println(String.format(fmt, paper.optionB));
            fmt = "   C、%s";
            System.out.println(String.format(fmt, paper.optionC));
            fmt = "   D、%s";
            System.out.println(String.format(fmt, paper.optionD));
        }


    }


    // 单项选择题
    void printMultiple(Paper paper, int num) {
        String fmt = "%d. %s";
        String question = paper.getQuestion();

        System.out.println(String.format(fmt, num, question));
        // 放在一行
        int length = countText(paper.optionA, paper.optionB, paper.optionC, paper.optionD, paper.optionE);

        // 选项较小
        if (length <= 60) {
            if (StringUtils.isEmpty(paper.optionE)) {
                fmt = "  A、%s  B、%s  C、%s  D、%s";
            } else {
                fmt = "  A、%s  B、%s  C、%s  D、%s  E、%s";
            }

            System.out.println(String.format(fmt, paper.optionA, paper.optionB, paper.optionC, paper.optionD, paper.optionE));
        }

        // 5行格式
        else {
            fmt = "   A、%s";
            System.out.println(String.format(fmt, paper.optionA));
            fmt = "   B、%s";
            System.out.println(String.format(fmt, paper.optionB));
            fmt = "   C、%s";
            System.out.println(String.format(fmt, paper.optionC));
            fmt = "   D、%s";
            System.out.println(String.format(fmt, paper.optionD));
            // E选项在判断是否为空
            if (!StringUtils.isEmpty(paper.optionE)) {
                fmt = "   E、%s";
                System.out.println(String.format(fmt, paper.optionE));

            }
        }


    }

    /**
     * 补空格
     *
     * @param text
     * @param length
     * @return
     */
    private String addBlank(String text, int length) {
        //
        int textlength = countText(text);
        if (textlength < length) {
            for (int i = 0; i < length - textlength; i++) {
                text += " ";
            }

        }
        return text;

    }


    /**
     * 处理数据
     *
     * @param paper
     */
    private void handlerData(Paper paper) {
        String question = paper.getQuestion();
        if (question.endsWith(".")) {
            question = question.substring(0, question.length() - 1);
        }
        if (!question.endsWith("。")) {
            question += "。";
        }
        paper.setQuestion(question);
    }

    /**
     * 判断题
     *
     * @param paper
     * @param num
     */
    void printTrue(Paper paper, int num) {
        String fmt = "（  ）%d. %s";
        System.out.println(String.format(fmt, num, paper.question));
    }


    /**
     * @param pickOn 这一轮被选中的认证点
     * @param type   题目类型
     * @param time   计数
     * @param topic  被选中的题目
     */
    private void choseTopic(Map<String, String> pickOn, String type, AtomicInteger time, List<Paper> topic) {
        List<Paper> candidate;
        // 首先肯定是不能这轮被选中的认证点
        List<Paper> middleList = map.entrySet().stream().filter(entry -> !pickOn.containsKey(entry.getKey())).flatMap(entry -> entry.getValue().stream()).
                // 第二肯定题型要对
                        filter(item -> item.getTypecode().equals(type)).collect(Collectors.toList());
        // 然后要判断题目被选中的次数来判断 ，先判断没被选中的情况，如果没有的话，计数加一，再来判断

        if (middleList.stream().filter(item -> item.getCount() <= time.intValue()).findFirst().isPresent()) {
            candidate = middleList.stream().filter(item -> item.getCount() <= time.intValue()).collect(Collectors.toList());
        } else {
            // 计数加1
            time.incrementAndGet();
            candidate = middleList.stream().filter(item -> item.getCount() <= time.intValue()).collect(Collectors.toList());

        }
        if (candidate.isEmpty()) {
            LOGGER.error("出错了，请检查代码,{}", time);
            return;
        }

        // 选中了
        int index = random(candidate.size());
        // 选中的题目
        Paper paper = candidate.get(index);
        topic.add(paper);
        // 认证点
        pickOn.put(paper.getCode(), paper.getCode());
        // 次数加一
        paper.setCount(paper.getCount() + 1);
    }

    private static int random(int size) {
        random.setSeed(seed[_i++ % seed.length]);
        return random.nextInt(size);
    }

/*    public static void main(String[] args) {
        int[] array = new int[]{200, 300, 100, 240, 130, 1, 2, 3, 5,2,100};
        for (Integer i : array) {
            System.out.println(random(i));
        }

    }*/

    public static int counthanzi(String text) {
        int amount = 0;
        for (int i = 0; i < text.length(); i++) {
            boolean matches = Pattern.matches("^[\u4E00-\u9Fa5]{0,}$", text.charAt(i) + "");
            if (matches) {
                amount++;
            }
        }
        return amount;
    }

    // 字符串长度
    private static int countText(String text) {
        return text.length() + counthanzi(text);
    }


    // 字符串长度
    private static int countText(String... text) {
        int num = 0;
        for (String t : text) {
            num += countText(t);
        }
        return num;
    }


}
